/* CsJni - .NET / Java Bridge
 * 
 * Copyright 2010 Robert E. Burke Licensed under the Apache License, Version 
 * 2.0 (the "License"); you may not use this file except in compliance with 
 * the License. You may obtain a copy of the License at 
 * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable 
 * law or agreed to in writing, software distributed under the License is 
 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 
 * KIND, either express or implied. See the License for the specific language 
 * governing permissions and limitations under the License. 
*/

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Diagnostics.CodeAnalysis;

namespace CsJni
{
    [StructLayout(LayoutKind.Sequential, Pack = 8)]
    internal struct JavaVMInitArgs
    {
        /* jint */ public int version;
        /* jint */ public int nOptions;
        /* JavaVMOption* */ public IntPtr options;
        /* jboolean */ public byte ignoreUnrecognized;
    }

    internal class JavaVMOptionArray
    {
        internal struct JavaVMOption
        {
            public string optionString;
            public IntPtr extraInfo;

            public JavaVMOption(string optionString, IntPtr extraInfo)
            {
                this.optionString = optionString;
                this.extraInfo = extraInfo;
            }
        }

        private List<JavaVMOption> mOptions = new List<JavaVMOption>();

        // Do not need to be IDisposable, Free method gets called and frees managd resources
        [SuppressMessage("Microsoft.Design", "CA1049:TypesThatOwnNativeResourcesShouldBeDisposable")]
        private class Pinned
        {
            [StructLayout(LayoutKind.Sequential)]
            private struct UnmanagedJavaVMOption : IDisposable
            {
                public IntPtr optionString;
                public IntPtr extraInfo;

                public UnmanagedJavaVMOption(string optionString, IntPtr extraInfo)
                {
                    this.optionString = Marshal.StringToHGlobalAnsi(optionString);
                    this.extraInfo = extraInfo;
                }

                #region IDisposable Members

                public void Dispose()
                {
                    Dispose(true);
                    GC.SuppressFinalize(this);
                }

                #endregion IDisposable Members

                private void Dispose(bool disposing)
                {
                    if (!disposing && !Environment.HasShutdownStarted)
                    {
                        throw new JniException(JniResourceManager.Instance.GetString("invalidDisposableFinalizer"));
                    }
                    if (optionString != IntPtr.Zero)
                    {
                        Marshal.FreeHGlobal(optionString);
                        optionString = IntPtr.Zero;
                    }
                }
            }

            private UnmanagedJavaVMOption[] m_array;
            private GCHandle m_gcHandle;
            private IntPtr m_address;
            public Pinned(List<JavaVMOption> list)
            {
                m_array = new UnmanagedJavaVMOption[list.Count];
                for (int i = 0; i < list.Count; i++)
                {
                    JavaVMOption option = list[i];
                    m_array[i] = new UnmanagedJavaVMOption(option.optionString, option.extraInfo);
                }
                m_gcHandle = GCHandle.Alloc(m_array, GCHandleType.Pinned);
                m_address = Marshal.UnsafeAddrOfPinnedArrayElement(m_array, 0);
            }
            public IntPtr Address
            {
                get { return m_address; }
            }
            public void Free()
            {
                if (m_address != IntPtr.Zero)
                {
                    m_gcHandle.Free();
                    m_address = IntPtr.Zero;
                }
                foreach (UnmanagedJavaVMOption option in m_array)
                {
                    option.Dispose();
                }
                m_array = null;
            }
        }

        private Pinned mPin;

        public JavaVMOptionArray()
        {
        }

        public void AddOption(string option)
        {
            CheckOption(option);
            mOptions.Add(new JavaVMOption(option, IntPtr.Zero));
        }

        public void AddReservedOption(string option, IntPtr extraInfo)
        {
            mOptions.Add(new JavaVMOption(option, extraInfo));
        }

        public int Length
        {
            get { return mOptions.Count; }
        }

        public IntPtr Pin()
        {
            Free();
            mPin = new Pinned(mOptions);
            return mPin.Address;
        }

        public void Free()
        {
            if (mPin != null)
            {
                mPin.Free();
                mPin = null;
            }
        }

        private static void CheckOption(string option)
        {
            if (!(option != null && option.Length != 0))
            {
                throw new ArgumentException(JniResourceManager.Instance.GetString("jvmCfgEmptyOption"));
            }
            if (option.IndexOf("java.class.path", StringComparison.Ordinal) != -1 || option.IndexOf("java.library.path", StringComparison.Ordinal) != -1)
            {
                throw new JniException(JniResourceManager.Instance.GetString("jvmCfgReservedOption"));
            }
            if (option == "vfprintf" || option == "exit" || option == "abort")
            {
                throw new JniException(JniResourceManager.Instance.GetString("jvmCfgReservedOption"));
            }
        }
    }
}
